﻿// /////////////////////////////////////////////////////////////////////
// 
//  Codeplex JSAnalyse
//  Copyright (c) 2011
//  JSAnalyse.Core   DependencyRunner.cs
// 
// /////////////////////////////////////////////////////////////////////
using System.Collections.Generic;
using System.IO;
using System.Linq;
using JSAnalyse.Core.Analyser;
using JSAnalyse.Core.Approver;
using JSAnalyse.Core.Interfaces;
using JSAnalyse.Core.Parser;

namespace JSAnalyse.Core
{
    /// <summary>
    /// Dependency Runner class which checks the correct and wrong dependencies for the given javascript files.
    /// </summary>
    public class DependencyRunner
    {
        #region Private variables

        private readonly List<JavascriptFileInfo> _javascriptFiles;
        private IAllowedDependencies _allowedDependencies;

        #endregion

        #region Constructor

        /// <summary>
        /// Initializes a new instance of the <see cref="DependencyRunner"/> class.
        /// </summary>
        /// <param name="javascriptFiles">The javascript files.</param>
        public DependencyRunner(IEnumerable<string> javascriptFiles)
        {
            _javascriptFiles = ConvertToJavascriptFileInfo(javascriptFiles);
            _allowedDependencies = new AllowedDependenciesXmlReader();
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="DependencyRunner"/> class.
        /// </summary>
        /// <param name="javascriptFiles">The javascript files.</param>
        /// <param name="allowedDependencies">The allowed dependencies.</param>
        public DependencyRunner(IEnumerable<string> javascriptFiles, IAllowedDependencies allowedDependencies)
        {
            _javascriptFiles = ConvertToJavascriptFileInfo(javascriptFiles);
            _allowedDependencies = allowedDependencies;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="DependencyRunner"/> class.
        /// </summary>
        /// <param name="javascriptFiles">The javascript files.</param>
        /// <param name="allowedDependencies">The allowed dependencies.</param>
        public DependencyRunner(List<JavascriptFileInfo> javascriptFiles, IAllowedDependencies allowedDependencies)
        {
            _javascriptFiles = javascriptFiles;
            _allowedDependencies = allowedDependencies;
        }

        #endregion

        #region Public methods

        /// <summary>
        /// Starts the dependency runner
        /// </summary>
        /// <returns></returns>
        public List<DependencyRunnerResult> Start()
        {
            var result = new List<DependencyRunnerResult>();
            var parserResultList = ParseAllJavascriptFiles();

            var analyserInputs = ConvertToAnalyserInput(parserResultList);
            foreach (var analyserInput in analyserInputs)
            {
                var dependencyAnalyser = new DependencyAnalyser(_allowedDependencies, analyserInput, analyserInputs.Where(item => item != analyserInput).ToList());
                var dependencyResult = dependencyAnalyser.Analyse();
                result.Add(CreateDependencyRunnerResult(analyserInput.SourceFile, dependencyResult.WrongFileDependencies));
            }
            return result;
        }

        private DependencyRunnerResult CreateDependencyRunnerResult(string sourceFile, List<DependencyInfo> wrongFileDependencies)
        {
            var result = new DependencyRunnerResult 
            {
                SourceFile = sourceFile,
                WrongFileDependencies = wrongFileDependencies 
            };

            foreach (var wrongFileDependency in result.WrongFileDependencies)
            {
                var javascriptFileInfo = _javascriptFiles.FirstOrDefault(item => item.File == sourceFile);
                if (javascriptFileInfo != null)
                {
                    string line = ReadLineFromString(javascriptFileInfo.FileContent, wrongFileDependency.DependencySourceLocation.Line);
                    wrongFileDependency.DependencySourceLocation.SourceLine = line;
                }
            }

            return result;
        }

        private string ReadLineFromString(string content, int lineNumber)
        {
            string result = string.Empty;
            
            using (var reader = new StringReader(content)) 
            { 
                string line;
                int i = 1;
                while ((line = reader.ReadLine()) != null)
                {
                    if (i == lineNumber)
                    {
                        result = line;
                        break;
                    }
                    i++;
                } 
            }
            return result;
        }

        #endregion

        #region Private methods
        private List<ParserResult> ParseAllJavascriptFiles()
        {
            return (from file in _javascriptFiles 
                    let parser = new JavascriptParser() 
                    select parser.Parse(file)).ToList();
        }

        /// <summary>
        /// Converts to javascript file info.
        /// </summary>
        /// <param name="javascriptFiles">The javascript files.</param>
        /// <returns></returns>
        /// <remarks></remarks>
        private static List<JavascriptFileInfo> ConvertToJavascriptFileInfo(IEnumerable<string> javascriptFiles)
        {
            return (from item in javascriptFiles 
                    select new JavascriptFileInfo(item)).ToList();
        }

        private static List<AnalyserInput> ConvertToAnalyserInput(List<ParserResult> parserResults)
        {
            return parserResults.Select(ConvertToAnalyserInput).ToList();
        }

        private static AnalyserInput ConvertToAnalyserInput(ParserResult parserResults)
        {
            var result = new AnalyserInput(parserResults.SourceFile);
            foreach(var item in parserResults.CallDependencies)
            {
                result.CallDependencies.Add(new CallDependencyInfo(item.Identifier, item.MemberSourceLocation));
            }
            foreach(var item in parserResults.Declarations)
            {
                result.Declarations.Add(new CallDependencyInfo(item.Identifier, item.MemberSourceLocation));
            }
            return result;
        }

        #endregion
    }
}
